01A isolates experiments

if (run_scripts) source("script/01A-isolates_experiments-01-metadata.R")

Isolate IDs

68 isolates were used in the pairwise competition assay. The csv contains:

  • ExpID: isolation ID used by Djordje. The first and second numbers indicate inoculum and replicates in Goldford2018.
  • ID: Sanger sequencing ID. Unique ID for sequenced isolates from Sanchez lab.
  • Community: community ID used in the competition assay.
  • Isolate: Isolates ID within a community ID, used in the competition assay.
isolates_ID_match <- read_csv("~/Dropbox/lab/emergent-coexistence/data/temp/isolates_ID_match.csv", col_types = cols())
isolates_ID_match

Community metadata

Community IDs and basic information

13 self-assembled communities, two across-community assembly, two random assmebly

communities <- read_csv("~/Dropbox/lab/emergent-coexistence/data/output/communities.csv", col_types = cols())
communities

01B isolates sanger sequencing

Read isolate 16S sequences

Read and merge ab1 files

This script reads the raw ab1 file from Sanger sequences and merges the forward and backward sequences. It may take ~20 minutes to read all 420 sequences from 210 isolates.

#' This script takes ~10mins to run
#' Do not run this script if `isolates_16S` is already available. 
if (run_scripts) source("script/01B-isolate_sanger_seq-01-read_align.R")

These isolates include Djordje’s isolates from simplicity paper, and Jean’s isolates from random environments. - ID: Sanger sequencing ID. - Sequence: Sanger sequence. - ConsensusLength:

isolates_16S <- read_csv("~/Dropbox/lab/emergent-coexistence/data/temp/isolates_16S.csv", col_types = cols())
isolates_16S

Taxonomy classifier

Use RDP taxonomy classifier to determine the isolates’ taxonomy. Assign fermenter/respirator and gram positive/negative according to families.

# This may take a few seconds
if (run_scripts) source("script/01B-isolate_sanger_seq-02-RDP.R")
  • ID
  • Fermenter: Fermenting sugar or not accroding to the taxanomy
  • GramPositive: gram positive or negative.
  • Family
  • Genus
  • GenusScore
  • Sequence

Multiple sequence alignment and make tree

Save the multiple sequence alignment result, ggtree-compatible tree, and the isolate RDP information with the tree in data/temp/isolates_sanger_seq.Rdata.

  • aln
  • aln2
  • tree
  • isolates_seq
if (run_scripts) source("script/01B-isolate_sanger_seq-03-make_tree.R")
load("~/Dropbox/lab/emergent-coexistence/data/temp/isolates_sanger_seq.Rdata")
isolates_seq
DNAStringSet object of length 68:
     width seq                                                                                         names               
 [1]   657 TACGGGAGGCAGCAGTGGGGAATATTGCACAATGGGCGCAAGCC...TTAATTCGATGCAACGCGAAGAACCTTACCTACTCTTGACATCC 1.2.A.1
 [2]   826 TACGGGAGGCAGCAGTGGGGAATATTGGACAATGGGCGAAAGCC...TTACCAGCACGTTATGGTGGGCACTCTAAGGAGACTGCCGGTGA 1.4.A.1
 [3]   525 GACGTTACCCGCAGAAGAAGCACCGGCTAACTCCGTGCCAGCAG...CGATGCAACGCGAAGAACCTTACCTACTCTTGACATCCAGAGAA 1.4.A.3
 [4]   790 TACGGGAGGCAGCAGTGGGGAATATTGCACAATGGGCGCAAGCC...GTTAAGTCCCGCAACGAGCGCAACCCTTATCCTTTGTTGCCAGC 1.4.B.4
 [5]   741 TCACCAAGGCGACGATCCGTAACTGGTCTGAGAGGATGATCAGT...AGCAACGCGAAGAACCTTACCAGGCCTTGACATGCAGAGAACTT 1.6.A.1
 ...   ... ...
[64]   716 TCCCTAGCTGGTCTGAGAGGATGACCAGCCACACTGGAACTGAG...TTTAATTCGATGCAACGCGAAGAACCTTACCTACTCTTGACATC 11.2.B.8.2
[65]   838 TGAGAGGATGACCAGCCACACTGGAACTGAGACACGGTCCAGAC...TTAAGTCCCGCAACGAGCGCAACCCTTATCCTTTGTTGCCAGCG 11.1.C.1
[66]   676 TACGGGAGGCAGCAGTGGGGAATATTGGACAATGGGCGAAAGCC...AGAACCTTACCAGGCCTTGACATGCAGAGAACTTTCCAGAGATG 11.5.B.2
[67]   772 NNNNNNNNNNNNNNNNNNNNNTGCAANTCGAGCGGCAGCGGGAA...NGCGAAAGCGTGGGGAGCAAACAGGATTAGATACCCTGGGTANN 2.6.A.5
[68]   803 NNNNNNGNNNNNNNNNNGCAGTCGAGCGGTAGCACAGGGAGCTT...GTCCACGCCGTAAACGATGTCGACTTGGNNNTTGTGCCCNTGNN 10.2.C.4

Plot basic tree

01C isolates OD and CFU conversion

Read OD (isolates and pairs)

Read the OD data generated from the analysis script for each experiment batch. The table below is the communities in each experimental batch.

Experimental batch Community
B2 C11R1 (except for isolate 1), C10R2, C2R6, C2R8, C7R1, C8R4
C C11R1 isolate 1 (the OD data is in OD_C2.csv)
C2 C11R2
D2 C11R5, C1R2, C1R4, C1R6, C1R7, C4R1

Read the OD files and export an overall OD data in csv output/OD.csv and an isolate OD csv output/isolates_OD.csv

if (run_scripts) source("script/01C-isolates_OD_CFU-01-read_OD.R")

Isolate OD at each transfer. Abs values have been averaged out among replicates.

There are 68 isolates used in the pairwise competition experiments.

Write all OD data in data/temp/OD.csv and isolate OD in data/temp/isolates_OD.csv

isolates_OD <- read_csv("~/Dropbox/lab/emergent-coexistence/data/temp/isolates_OD.csv", col_types = cols())
isolates_OD

Read isolates CFU

if (run_scripts) source("script/01C-isolates_OD_CFU-02-read_CFU.R")

Colony counts at the given dilution factors.

There are 68 isolates used in the pairwise competition experiments.

Write isolate OD and CFU in data/temp/isolates_OD_CFU.csv

Epsilon: OD-CFU conversion coefficient

if (run_scripts) source("script/01C-isolates_OD_CFU-03-epsilon.R")

To determine the outcomes of pairwise competition, I compare the frequencies changes between T1 and T8, which were empirically measured at different approaches and have to be converted into the same unit for comparison.

  • T1 frequencies are determined by mixing two isolate inocula with the standardized OD (OD620 = 0.1) at three initial frequencies: 5:95, 50:50, 95:5. T1 frequencies are thus OD frequencies.

  • T8 frequencies are determined by plating the mature media (48 hour growth cycle) on rich agar plates and counting the colony of each isolate. T8 frequencies are thus CFU frequencies.

In this section, I convert the OD frequencies at T1 to CFU frequencies. For each isolate, the CFU and OD have the following relationships.

\[CFU_A = \frac{OD_A DF_A v_A}{\epsilon_A}\]

\[CFU_A = \epsilon_A OD_A DF_A v_A\]

where \(OD\) is the measured OD at 620 nm, \(DF\) is the dilution factor (\(10^4\) or \(10^5\)), \(v\) is the plating volume (20 uL for all experiment) \(\epsilon\) is the conversion coefficient between OD and CFU.

isolates_epsilon <- read_csv("~/Dropbox/lab/emergent-coexistence/data/temp/isolates_epsilon.csv", col_types = cols())
isolates_epsilon

01D isolates growth rates

if (run_scripts) source("script/01D-isolates_growth_rate-01-read_data.R")
isolates_growth_traits <- read_csv("~/Dropbox/lab/emergent-coexistence/data/temp/isolates_growth_traits.csv", col_type = cols())
isolates_growth_traits

isolates_growth_traits contains many columns. CS is a wildcard for any carbon source measured. - r_CS_*hr: growth rates on carbon sources at 12, 16, 28, 48 hr, measured from the growth curve data. Data from Sylvie data/raw/growth_rate/raw_gcurves_all_sylvies.csv. - X_CS_*hr: secreted acids (acetate, lactate, succinate) amount at 0, 16, 28, 48 hr. Data from Sylvie data/raw/growth_rate/Estrela_2021_isolates_ph_OAs.csv - pH_*hr: pH measured at 0, 16, 28, 48 hr. Data from Sylvie data/raw/growth_rate/Estrela_2021_isolates_ph_OAs.csv. - PreferredCS and SecretedCS: acid preference determined by the sequence of acid used, manually corrected by eyeballing the figure 01D-byproduct_conc.png. Data from Sylvie data/raw/growth_rate/Estrela_2021_isolates_ph_OAs.csv. - ByproductSum_*hr: sum of acids produced at 0, 16, 28, 48 hr. Data from Sylvie data/raw/growth_rate/raw_gcurves_all_sylvies.

01E match isolates to ESV for community abundanuce

Read community ESV sequences

if (run_scripts) source("script/01E-match_community_abundance-01-community_sequence.R")

OTU table output communities_abundance comes from the curated community ESV table from Sylvie.

I formatted the table so that it has the following variables:

Match community ESVs and isolate Sanger sequences

# It may take 10 mins
if (run_scripts) source("script/01E-match_community_abundance-02-match_isolate_16S.R")

IUPAC notation for DNA base pairs

W, S, M, K, R, Y represent two possibilities for one base pair B, D, H, V represent three possiblities N means any nucleotides (but not a gap)

I tested five alignment methods in Biostrings::pairwiseAlignment()

sequences_alignment <- read_csv("~/Dropbox/lab/emergent-coexistence/data/temp/sequences_alignment.csv", col_type = cols())
sequences_alignment

Relative abundance explained by the sequences

if (run_scripts) source("script/01E-match_community_abundance-03-matched_abundance.R")

ESV abundances of 13 communities used in the competition assay

plot_abundance <- function(df, label_x = "Community", label_y = "RelativeAbundance", fill = "CommunityESVID") {
    ggplot(df) +
        geom_bar(aes_string(x = label_x, y = label_y, fill = fill),
                 position = "stack", stat = "identity", col = 1) +
        theme_bw() +
        theme(axis.text.x = element_text(angle = 90)) +
        scale_y_continuous(expand = c(0, 0))
}
communities_abundance <- read_csv("~/Dropbox/lab/emergent-coexistence/data/temp/communities_abundance.csv", col_types = cols())
communities_abundance %>% plot_abundance(label_x = "Community", label_y = "RelativeAbundance", fill = "ESVFamily")

Abundances of ESVs matched to Sangers

isolates_abundance %>%
    left_join(isolates_RDP) %>%
    mutate(Community = ordered(Community,  communities$Community)) %>%
    plot_abundance(label_x = "Community", label_y = " RelativeAbundance", fill = "Family") +
    labs(x = "", y = "Relative abundance") +
    scale_fill_manual(values = setNames(color_sets$Color, color_sets$Family)) +
    scale_x_discrete(expand=c(0,0)) +
    scale_y_continuous(expand=c(0,0), limits = c(0,1), breaks = seq(0,1, .25)) 
Joining, by = "ExpID"
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing scale.
Warning: Removed 20 rows containing missing values (position_stack).

Summary

Combine and generate the isolate metadata file

if (run_scripts) source("script/01-isolates-01-read_match_isolate.R")

Isolates information in one data.frame isolates. 68 isolates

isolates <- read_csv("~/Dropbox/lab/emergent-coexistence/data/output/isolates.csv", col_types = cols())
isolates

Plot isolates’ functions (Thies section is deprecated)

Growth rates on varied CSs

isolates %>%
    select(ID, Fermenter, starts_with("r_")) %>%
    pivot_longer(cols = c(-Fermenter, -ID), names_prefix = "r_", names_to = "CS", values_to = "r") %>%
    mutate(CS = factor(CS, c("glucose", "acetate", "lactate", "succinate", "gluconate", "ketogluconate"))) %>%
    filter(!is.na(Fermenter)) %>%
    ggplot() +
    geom_histogram(aes(x = r, fill = Fermenter), color = 1, binwidth = 0.005) +
    facet_wrap(.~CS, nrow = 3, scales = "free_y") +
    theme_classic()
Warning: Removed 846 rows containing non-finite values (stat_bin).

OD at 16hr on varied CSs

isolates %>%
    select(ID, Fermenter, starts_with("OD")) %>%
    pivot_longer(cols = c(-Fermenter, -ID), names_prefix = "OD620_16h_", names_to = "CS", values_to = "r") %>%
    mutate(CS = factor(CS, c("glucose", "acetate", "succinate", "citrate", "fructose", "arabinose", "galactose", "ribose", "glycerol", "pyruvate", "malate", "fumarate"))) %>%
    filter(!is.na(Fermenter)) %>%
    ggplot() +
    geom_histogram(aes(x = r, fill = Fermenter), color = 1, binwidth = 0.01) +
    facet_wrap(.~CS, nrow = 4, scales = "free_y") +
    theme_classic()
Warning: Removed 720 rows containing non-finite values (stat_bin).

Acids production

isolates %>%
    select(ID, Fermenter, starts_with("X_"), starts_with("pH")) %>%
    pivot_longer(cols = c(-Fermenter, -ID), names_prefix = "X_", names_sep = "_", names_to = c("CS", "Time"), values_to = "Amount") %>%
    mutate(Time = sub("hr", "", Time)) %>%
    mutate(CS = factor(CS, c("pH", "acetate", "lactate", "succinate"))) %>%
    filter(!is.na(Fermenter)) %>%
    ggplot() +
    geom_line(aes(x = Time, y = Amount, group = ID, color = Fermenter)) +
    facet_wrap(.~CS, nrow = 2, scales = "free_y") +
    theme_classic()
Warning: Removed 180 row(s) containing missing values (geom_path).

LS0tCnRpdGxlOiAiQW5hbHlzaXMgb24gaXNvbGF0ZXMgdXNlZCBpbiBwYWlyd2lzZSBjb21wZXRpdGlvbnMiCmF1dGhvcjogIkNoYW5nLVl1IENoYW5nIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICBodG1sX25vdGVib29rOiAgCiAgICB0b2M6IHllcwogICAgbnVtYmVyX3NlY3Rpb25zOiBubwotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlID0gRn0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGNhY2hlID0gRkFMU0UsIGVjaG8gPSBUUlVFKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKcnVuX3NjcmlwdHMgPC0gRgpgYGAKCgojIDAxQSBpc29sYXRlcyBleHBlcmltZW50cwoKYGBge3J9CmlmIChydW5fc2NyaXB0cykgc291cmNlKCJzY3JpcHQvMDFBLWlzb2xhdGVzX2V4cGVyaW1lbnRzLTAxLW1ldGFkYXRhLlIiKQpgYGAKCgojIyBJc29sYXRlIElEcwoKNjggaXNvbGF0ZXMgd2VyZSB1c2VkIGluIHRoZSBwYWlyd2lzZSBjb21wZXRpdGlvbiBhc3NheS4gVGhlIGNzdiBjb250YWluczoKCi0gYEV4cElEYDogaXNvbGF0aW9uIElEIHVzZWQgYnkgRGpvcmRqZS4gVGhlIGZpcnN0IGFuZCBzZWNvbmQgbnVtYmVycyBpbmRpY2F0ZSBpbm9jdWx1bSBhbmQgcmVwbGljYXRlcyBpbiBHb2xkZm9yZDIwMTguCi0gYElEYDogU2FuZ2VyIHNlcXVlbmNpbmcgSUQuIFVuaXF1ZSBJRCBmb3Igc2VxdWVuY2VkIGlzb2xhdGVzIGZyb20gU2FuY2hleiBsYWIuCi0gYENvbW11bml0eWA6IGNvbW11bml0eSBJRCB1c2VkIGluIHRoZSBjb21wZXRpdGlvbiBhc3NheS4KLSBgSXNvbGF0ZWA6IElzb2xhdGVzIElEIHdpdGhpbiBhIGNvbW11bml0eSBJRCwgdXNlZCBpbiB0aGUgY29tcGV0aXRpb24gYXNzYXkuCgoKYGBge3J9Cmlzb2xhdGVzX0lEX21hdGNoIDwtIHJlYWRfY3N2KCJ+L0Ryb3Bib3gvbGFiL2VtZXJnZW50LWNvZXhpc3RlbmNlL2RhdGEvdGVtcC9pc29sYXRlc19JRF9tYXRjaC5jc3YiLCBjb2xfdHlwZXMgPSBjb2xzKCkpCmlzb2xhdGVzX0lEX21hdGNoCmBgYAoKIyMgQ29tbXVuaXR5IG1ldGFkYXRhCgpDb21tdW5pdHkgSURzIGFuZCBiYXNpYyBpbmZvcm1hdGlvbgoKMTMgc2VsZi1hc3NlbWJsZWQgY29tbXVuaXRpZXMsIHR3byBhY3Jvc3MtY29tbXVuaXR5IGFzc2VtYmx5LCB0d28gcmFuZG9tIGFzc21lYmx5CgpgYGB7cn0KY29tbXVuaXRpZXMgPC0gcmVhZF9jc3YoIn4vRHJvcGJveC9sYWIvZW1lcmdlbnQtY29leGlzdGVuY2UvZGF0YS9vdXRwdXQvY29tbXVuaXRpZXMuY3N2IiwgY29sX3R5cGVzID0gY29scygpKQpjb21tdW5pdGllcwpgYGAKCiMgMDFCIGlzb2xhdGVzIHNhbmdlciBzZXF1ZW5jaW5nCgotICAgUmVhZCBhbmQgYWxpZ24gaXNvbGF0ZSAxNlMgc2VxdWVuY2VzLiAxKSBSZWFkIHJhdyBgLmFiMWAgZmlsZSBhbmQgMikgTWVyZ2UgRiBhbmQgUiBzZXF1ZW5jZXMuCi0gICBDbGFzc2lmeSBpc29sYXRlIHRheG9ub215IHVzaW5nIFJEUCBjbGFzc2lmaWVyLiBgclJEUGAKLSAgIEFsaWduIHNlcWV1bmNlcyB1c2luZyBNVVNDTEUuIGBtc2FgCi0gICBCdWlsZCBwaHlsb2dlbmV0aWMgdHJlZSBieSB1c2luZyBGYXN0VHJlZSBvciBSQXhNTC4gYGdndHJlZWAKCiMjIFJlYWQgaXNvbGF0ZSAxNlMgc2VxdWVuY2VzCgpSZWFkIGFuZCBtZXJnZSBhYjEgZmlsZXMKClRoaXMgc2NyaXB0IHJlYWRzIHRoZSByYXcgYWIxIGZpbGUgZnJvbSBTYW5nZXIgc2VxdWVuY2VzIGFuZCBtZXJnZXMgdGhlIGZvcndhcmQgYW5kIGJhY2t3YXJkIHNlcXVlbmNlcy4gSXQgbWF5IHRha2UgXH4yMCBtaW51dGVzIHRvIHJlYWQgYWxsIDQyMCBzZXF1ZW5jZXMgZnJvbSAyMTAgaXNvbGF0ZXMuCgpgYGB7cn0KIycgVGhpcyBzY3JpcHQgdGFrZXMgfjEwbWlucyB0byBydW4KIycgRG8gbm90IHJ1biB0aGlzIHNjcmlwdCBpZiBgaXNvbGF0ZXNfMTZTYCBpcyBhbHJlYWR5IGF2YWlsYWJsZS4gCmlmIChydW5fc2NyaXB0cykgc291cmNlKCJzY3JpcHQvMDFCLWlzb2xhdGVfc2FuZ2VyX3NlcS0wMS1yZWFkX2FsaWduLlIiKQpgYGAKClRoZXNlIGlzb2xhdGVzIGluY2x1ZGUgRGpvcmRqZSdzIGlzb2xhdGVzIGZyb20gc2ltcGxpY2l0eSBwYXBlciwgYW5kIEplYW4ncyBpc29sYXRlcyBmcm9tIHJhbmRvbSBlbnZpcm9ubWVudHMuCi0gYElEYDogU2FuZ2VyIHNlcXVlbmNpbmcgSUQuCi0gYFNlcXVlbmNlYDogU2FuZ2VyIHNlcXVlbmNlLgotIGBDb25zZW5zdXNMZW5ndGhgOiAKCmBgYHtyfQppc29sYXRlc18xNlMgPC0gcmVhZF9jc3YoIn4vRHJvcGJveC9sYWIvZW1lcmdlbnQtY29leGlzdGVuY2UvZGF0YS90ZW1wL2lzb2xhdGVzXzE2Uy5jc3YiLCBjb2xfdHlwZXMgPSBjb2xzKCkpCmlzb2xhdGVzXzE2UwpgYGAKCiMjIFRheG9ub215IGNsYXNzaWZpZXIKClVzZSBSRFAgdGF4b25vbXkgY2xhc3NpZmllciB0byBkZXRlcm1pbmUgdGhlIGlzb2xhdGVzJyB0YXhvbm9teS4gQXNzaWduIGZlcm1lbnRlci9yZXNwaXJhdG9yIGFuZCBncmFtIHBvc2l0aXZlL25lZ2F0aXZlIGFjY29yZGluZyB0byBmYW1pbGllcy4gCgpgYGB7cn0KIyBUaGlzIG1heSB0YWtlIGEgZmV3IHNlY29uZHMKaWYgKHJ1bl9zY3JpcHRzKSBzb3VyY2UoInNjcmlwdC8wMUItaXNvbGF0ZV9zYW5nZXJfc2VxLTAyLVJEUC5SIikKYGBgCgotIGBJRGAKLSBgRmVybWVudGVyYDogRmVybWVudGluZyBzdWdhciBvciBub3QgYWNjcm9kaW5nIHRvIHRoZSB0YXhhbm9teQotIGBHcmFtUG9zaXRpdmVgOiBncmFtIHBvc2l0aXZlIG9yIG5lZ2F0aXZlLgotIGBGYW1pbHlgCi0gYEdlbnVzYAotIGBHZW51c1Njb3JlYAotIGBTZXF1ZW5jZWAKCmBgYHtyfQppc29sYXRlc19SRFAgPC0gcmVhZF9jc3YoIn4vRHJvcGJveC9sYWIvZW1lcmdlbnQtY29leGlzdGVuY2UvZGF0YS90ZW1wL2lzb2xhdGVzX1JEUC5jc3YiLCBjb2xfdHlwZXMgPSBjb2xzKCkpCmlzb2xhdGVzX1JEUApgYGAKCiMjIE11bHRpcGxlIHNlcXVlbmNlIGFsaWdubWVudCBhbmQgbWFrZSB0cmVlCgpTYXZlIHRoZSBtdWx0aXBsZSBzZXF1ZW5jZSBhbGlnbm1lbnQgcmVzdWx0LCBnZ3RyZWUtY29tcGF0aWJsZSB0cmVlLCBhbmQgdGhlIGlzb2xhdGUgUkRQIGluZm9ybWF0aW9uIHdpdGggdGhlIHRyZWUgaW4gYGRhdGEvdGVtcC9pc29sYXRlc19zYW5nZXJfc2VxLlJkYXRhYC4KCi0gYGFsbmAKLSBgYWxuMmAKLSBgdHJlZWAKLSBgaXNvbGF0ZXNfc2VxYAoKYGBge3J9CmlmIChydW5fc2NyaXB0cykgc291cmNlKCJzY3JpcHQvMDFCLWlzb2xhdGVfc2FuZ2VyX3NlcS0wMy1tYWtlX3RyZWUuUiIpCmBgYAoKYGBge3J9CmxvYWQoIn4vRHJvcGJveC9sYWIvZW1lcmdlbnQtY29leGlzdGVuY2UvZGF0YS90ZW1wL2lzb2xhdGVzX3Nhbmdlcl9zZXEuUmRhdGEiKQppc29sYXRlc19zZXEKYGBgCgoKUGxvdCBiYXNpYyB0cmVlCgpgYGB7ciBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTB9CmdndHJlZSh0cmVlKSArIGdncGxvdDI6OnhsaW0oMCwgMC41KQpgYGAKCgoKIyAwMUMgaXNvbGF0ZXMgT0QgYW5kIENGVSBjb252ZXJzaW9uCgotICAgUmVhZCBPRHMgb2YgaXNvbGF0ZXMgYW5kIHBhaXJzIHRoYXQgd2VyZSBtZWFzdXJlZCBpbiBwYWlyd2lzZSBjb21wZXRpdGlvbiBleHBlcmltZW50LiBgaXNvbGF0ZXNfT0QuY3N2YAotICAgUmVhZCBpc29sYXRlcycgQ0ZVIG9uIG1vbm9jdWx0dXJlIGBpc29sYXRlc19DRlVfVDguY3N2YAotICAgTWF0Y2ggaXNvbGF0ZXMnIE9EIGFuZCBDRlUgYGlzb2xhdGVzX09EX0NGVS5jc3ZgCi0gICBDYWxjdWxhdGUgdW5jZXJ0YWludHkgaW4gZXBzaWxvbiB1c2luZyBlcnJvciBwcm9wYWdhdGlvbiB0aGVvcnkKCiMjIFJlYWQgT0QgKGlzb2xhdGVzIGFuZCBwYWlycykKClJlYWQgdGhlIE9EIGRhdGEgZ2VuZXJhdGVkIGZyb20gdGhlIGFuYWx5c2lzIHNjcmlwdCBmb3IgZWFjaCBleHBlcmltZW50IGJhdGNoLiBUaGUgdGFibGUgYmVsb3cgaXMgdGhlIGNvbW11bml0aWVzIGluIGVhY2ggZXhwZXJpbWVudGFsIGJhdGNoLgoKfCBFeHBlcmltZW50YWwgYmF0Y2ggfCBDb21tdW5pdHkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwtLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfAp8IEIyICAgICAgICAgICAgICAgICB8IEMxMVIxIChleGNlcHQgZm9yIGlzb2xhdGUgMSksIEMxMFIyLCBDMlI2LCBDMlI4LCBDN1IxLCBDOFI0IHwKfCBDICAgICAgICAgICAgICAgICAgfCBDMTFSMSBpc29sYXRlIDEgKHRoZSBPRCBkYXRhIGlzIGluIGBPRF9DMi5jc3ZgKSAgICAgICAgICAgICB8CnwgQzIgICAgICAgICAgICAgICAgIHwgQzExUjIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IEQyICAgICAgICAgICAgICAgICB8IEMxMVI1LCBDMVIyLCBDMVI0LCBDMVI2LCBDMVI3LCBDNFIxICAgICAgICAgICAgICAgICAgICAgICAgIHwKClJlYWQgdGhlIE9EIGZpbGVzIGFuZCBleHBvcnQgYW4gb3ZlcmFsbCBPRCBkYXRhIGluIGNzdiBgb3V0cHV0L09ELmNzdmAgYW5kIGFuIGlzb2xhdGUgT0QgY3N2IGBvdXRwdXQvaXNvbGF0ZXNfT0QuY3N2YAoKYGBge3J9CmlmIChydW5fc2NyaXB0cykgc291cmNlKCJzY3JpcHQvMDFDLWlzb2xhdGVzX09EX0NGVS0wMS1yZWFkX09ELlIiKQpgYGAKCklzb2xhdGUgT0QgYXQgZWFjaCB0cmFuc2Zlci4gQWJzIHZhbHVlcyBoYXZlIGJlZW4gYXZlcmFnZWQgb3V0IGFtb25nIHJlcGxpY2F0ZXMuCgpUaGVyZSBhcmUgNjggaXNvbGF0ZXMgdXNlZCBpbiB0aGUgcGFpcndpc2UgY29tcGV0aXRpb24gZXhwZXJpbWVudHMuCgpXcml0ZSBhbGwgT0QgZGF0YSBpbiBgZGF0YS90ZW1wL09ELmNzdmAgYW5kIGlzb2xhdGUgT0QgaW4gYGRhdGEvdGVtcC9pc29sYXRlc19PRC5jc3ZgCgpgYGB7cn0KaXNvbGF0ZXNfT0QgPC0gcmVhZF9jc3YoIn4vRHJvcGJveC9sYWIvZW1lcmdlbnQtY29leGlzdGVuY2UvZGF0YS90ZW1wL2lzb2xhdGVzX09ELmNzdiIsIGNvbF90eXBlcyA9IGNvbHMoKSkKaXNvbGF0ZXNfT0QKYGBgCgojIyBSZWFkIGlzb2xhdGVzIENGVQoKYGBge3J9CmlmIChydW5fc2NyaXB0cykgc291cmNlKCJzY3JpcHQvMDFDLWlzb2xhdGVzX09EX0NGVS0wMi1yZWFkX0NGVS5SIikKYGBgCgpDb2xvbnkgY291bnRzIGF0IHRoZSBnaXZlbiBkaWx1dGlvbiBmYWN0b3JzLgoKYGBge3J9Cmlzb2xhdGVzX09EX0NGVSA8LSByZWFkX2Nzdigifi9Ecm9wYm94L2xhYi9lbWVyZ2VudC1jb2V4aXN0ZW5jZS9kYXRhL3RlbXAvaXNvbGF0ZXNfT0RfQ0ZVLmNzdiIsIGNvbF90eXBlID0gY29scygpKQppc29sYXRlc19PRF9DRlUKYGBgCgpUaGVyZSBhcmUgNjggaXNvbGF0ZXMgdXNlZCBpbiB0aGUgcGFpcndpc2UgY29tcGV0aXRpb24gZXhwZXJpbWVudHMuCgpXcml0ZSBpc29sYXRlIE9EIGFuZCBDRlUgaW4gYGRhdGEvdGVtcC9pc29sYXRlc19PRF9DRlUuY3N2YAoKIyMgRXBzaWxvbjogT0QtQ0ZVIGNvbnZlcnNpb24gY29lZmZpY2llbnQKCmBgYHtyIHdhcm5pbmcgPSBGfQppZiAocnVuX3NjcmlwdHMpIHNvdXJjZSgic2NyaXB0LzAxQy1pc29sYXRlc19PRF9DRlUtMDMtZXBzaWxvbi5SIikKYGBgCgpUbyBkZXRlcm1pbmUgdGhlIG91dGNvbWVzIG9mIHBhaXJ3aXNlIGNvbXBldGl0aW9uLCBJIGNvbXBhcmUgdGhlIGZyZXF1ZW5jaWVzIGNoYW5nZXMgYmV0d2VlbiBUMSBhbmQgVDgsIHdoaWNoIHdlcmUgZW1waXJpY2FsbHkgbWVhc3VyZWQgYXQgZGlmZmVyZW50IGFwcHJvYWNoZXMgYW5kIGhhdmUgdG8gYmUgY29udmVydGVkIGludG8gdGhlIHNhbWUgdW5pdCBmb3IgY29tcGFyaXNvbi4KCi0gICBUMSBmcmVxdWVuY2llcyBhcmUgZGV0ZXJtaW5lZCBieSBtaXhpbmcgdHdvIGlzb2xhdGUgaW5vY3VsYSB3aXRoIHRoZSBzdGFuZGFyZGl6ZWQgT0QgKE9ENjIwID0gMC4xKSBhdCB0aHJlZSBpbml0aWFsIGZyZXF1ZW5jaWVzOiA1Ojk1LCA1MDo1MCwgOTU6NS4gVDEgZnJlcXVlbmNpZXMgYXJlIHRodXMgT0QgZnJlcXVlbmNpZXMuCgotICAgVDggZnJlcXVlbmNpZXMgYXJlIGRldGVybWluZWQgYnkgcGxhdGluZyB0aGUgbWF0dXJlIG1lZGlhICg0OCBob3VyIGdyb3d0aCBjeWNsZSkgb24gcmljaCBhZ2FyIHBsYXRlcyBhbmQgY291bnRpbmcgdGhlIGNvbG9ueSBvZiBlYWNoIGlzb2xhdGUuIFQ4IGZyZXF1ZW5jaWVzIGFyZSB0aHVzIENGVSBmcmVxdWVuY2llcy4KCkluIHRoaXMgc2VjdGlvbiwgSSBjb252ZXJ0IHRoZSBPRCBmcmVxdWVuY2llcyBhdCBUMSB0byBDRlUgZnJlcXVlbmNpZXMuIEZvciBlYWNoIGlzb2xhdGUsIHRoZSBDRlUgYW5kIE9EIGhhdmUgdGhlIGZvbGxvd2luZyByZWxhdGlvbnNoaXBzLgoKJCRDRlVfQSA9IFxmcmFje09EX0EgREZfQSB2X0F9e1xlcHNpbG9uX0F9JCQKCiQkQ0ZVX0EgPSBcZXBzaWxvbl9BIE9EX0EgREZfQSB2X0EkJAoKd2hlcmUgJE9EJCBpcyB0aGUgbWVhc3VyZWQgT0QgYXQgNjIwIG5tLCAkREYkIGlzIHRoZSBkaWx1dGlvbiBmYWN0b3IgKCQxMF40JCBvciAkMTBeNSQpLCAkdiQgaXMgdGhlIHBsYXRpbmcgdm9sdW1lICgyMCB1TCBmb3IgYWxsIGV4cGVyaW1lbnQpICRcZXBzaWxvbiQgaXMgdGhlIGNvbnZlcnNpb24gY29lZmZpY2llbnQgYmV0d2VlbiBPRCBhbmQgQ0ZVLgoKYGBge3J9Cmlzb2xhdGVzX2Vwc2lsb24gPC0gcmVhZF9jc3YoIn4vRHJvcGJveC9sYWIvZW1lcmdlbnQtY29leGlzdGVuY2UvZGF0YS90ZW1wL2lzb2xhdGVzX2Vwc2lsb24uY3N2IiwgY29sX3R5cGVzID0gY29scygpKQppc29sYXRlc19lcHNpbG9uCmBgYAoKIyAwMUQgaXNvbGF0ZXMgZ3Jvd3RoIHJhdGVzCgoKYGBge3J9CmlmIChydW5fc2NyaXB0cykgc291cmNlKCJzY3JpcHQvMDFELWlzb2xhdGVzX2dyb3d0aF9yYXRlLTAxLXJlYWRfZGF0YS5SIikKYGBgCgoKYGBge3J9Cmlzb2xhdGVzX2dyb3d0aF90cmFpdHMgPC0gcmVhZF9jc3YoIn4vRHJvcGJveC9sYWIvZW1lcmdlbnQtY29leGlzdGVuY2UvZGF0YS90ZW1wL2lzb2xhdGVzX2dyb3d0aF90cmFpdHMuY3N2IiwgY29sX3R5cGUgPSBjb2xzKCkpCmlzb2xhdGVzX2dyb3d0aF90cmFpdHMKYGBgCgpgaXNvbGF0ZXNfZ3Jvd3RoX3RyYWl0c2AgY29udGFpbnMgbWFueSBjb2x1bW5zLiBDUyBpcyBhIHdpbGRjYXJkIGZvciBhbnkgY2FyYm9uIHNvdXJjZSBtZWFzdXJlZC4KICAgIC0gYHJfQ1NfKmhyYDogZ3Jvd3RoIHJhdGVzIG9uIGNhcmJvbiBzb3VyY2VzIGF0IDEyLCAxNiwgMjgsIDQ4IGhyLCBtZWFzdXJlZCBmcm9tIHRoZSBncm93dGggY3VydmUgZGF0YS4gRGF0YSBmcm9tIFN5bHZpZSBgZGF0YS9yYXcvZ3Jvd3RoX3JhdGUvcmF3X2djdXJ2ZXNfYWxsX3N5bHZpZXMuY3N2YC4KICAgIC0gYFhfQ1NfKmhyYDogc2VjcmV0ZWQgYWNpZHMgKGFjZXRhdGUsIGxhY3RhdGUsIHN1Y2NpbmF0ZSkgYW1vdW50IGF0IDAsIDE2LCAyOCwgNDggaHIuIERhdGEgZnJvbSBTeWx2aWUgYGRhdGEvcmF3L2dyb3d0aF9yYXRlL0VzdHJlbGFfMjAyMV9pc29sYXRlc19waF9PQXMuY3N2YAogICAgLSBgcEhfKmhyYDogcEggbWVhc3VyZWQgYXQgMCwgMTYsIDI4LCA0OCBoci4gRGF0YSBmcm9tIFN5bHZpZSBgZGF0YS9yYXcvZ3Jvd3RoX3JhdGUvRXN0cmVsYV8yMDIxX2lzb2xhdGVzX3BoX09Bcy5jc3ZgLgogICAgLSBgUHJlZmVycmVkQ1NgIGFuZCBgU2VjcmV0ZWRDU2A6IGFjaWQgcHJlZmVyZW5jZSBkZXRlcm1pbmVkIGJ5IHRoZSBzZXF1ZW5jZSBvZiBhY2lkIHVzZWQsIG1hbnVhbGx5IGNvcnJlY3RlZCBieSBleWViYWxsaW5nIHRoZSBmaWd1cmUgYDAxRC1ieXByb2R1Y3RfY29uYy5wbmdgLiBEYXRhIGZyb20gU3lsdmllIGBkYXRhL3Jhdy9ncm93dGhfcmF0ZS9Fc3RyZWxhXzIwMjFfaXNvbGF0ZXNfcGhfT0FzLmNzdmAuCiAgICAtIGBCeXByb2R1Y3RTdW1fKmhyYDogc3VtIG9mIGFjaWRzIHByb2R1Y2VkIGF0IDAsIDE2LCAyOCwgNDggaHIuIERhdGEgZnJvbSBTeWx2aWUgIGBkYXRhL3Jhdy9ncm93dGhfcmF0ZS9yYXdfZ2N1cnZlc19hbGxfc3lsdmllc2AuCgoKCiMgMDFFIG1hdGNoIGlzb2xhdGVzIHRvIEVTViBmb3IgY29tbXVuaXR5IGFidW5kYW51Y2UKCgpSZWFkIGNvbW11bml0eSBFU1Ygc2VxdWVuY2VzCgpgYGB7cn0KaWYgKHJ1bl9zY3JpcHRzKSBzb3VyY2UoInNjcmlwdC8wMUUtbWF0Y2hfY29tbXVuaXR5X2FidW5kYW5jZS0wMS1jb21tdW5pdHlfc2VxdWVuY2UuUiIpCmBgYAoKT1RVIHRhYmxlIG91dHB1dCBgY29tbXVuaXRpZXNfYWJ1bmRhbmNlYCBjb21lcyBmcm9tIHRoZSBjdXJhdGVkIGNvbW11bml0eSBFU1YgdGFibGUgZnJvbSBTeWx2aWUuCgpJIGZvcm1hdHRlZCB0aGUgdGFibGUgc28gdGhhdCBpdCBoYXMgdGhlIGZvbGxvd2luZyB2YXJpYWJsZXM6CgotICAgYFNhbXBsZUlEYDogZXhwZXJpbWVudCBJRCB1c2VkIGJ5IE5hbnhpIG9yIFN5bHZpZS4KLSAgIGBDb21tdW5pdHlgOiBjb21tdW5pdHksIGZvciBpbnN0YW5jZSwgQzJSNCBvciBDMVJwb29sLgotICAgYFRyYW5zZmVyYDogdGhlIHRyYW5zZmVyIHdoZW4gdGhlIGNvbW11bml0eSB3YXMgc2VxdWVuY2VkLgotICAgYEFkdW5kYW5jZWA6IHRoZSBudW1iZXIgb2YgdGhpcyBzZXF1ZW5jZSBpbiB0aGUgY29tbXVuaXR5LgotICAgYFJlbGF0aXZlQWJ1bmRhbmNlYAotICAgYENvbW11bml0eUVTVklEYDogdGhlIHNlcXVlbmNlIGlkZW50aWZpZXIuIFRoaXMgaWRlbnRpZmllciBpcyBjb21tdW5pdHktc2VxdWVuY2Ugc3BlY2lmaWMuCi0gICBgRVNWYDogdGhlIEROQSBzZXF1ZW5jZSBpbiAxNnMgVjQgcmVnaW9uLgotICAgYENhcmJvblNvdXJjZWA6IHRoZSBjYXJib24gc291cmNlIHVzZWQgZm9yIGFzc2VtYmx5Ci0gICBgT3JkZXJgLCBgRmFtaWx5YCwgYW5kIGBHZW51c2A6IFNJTFZBIGFzc2lnbmVkIHRheG9ub215CgoKYGBge3IgZWNobyA9IFR9CmNvbW11bml0aWVzX2FidW5kYW5jZSA8LSByZWFkX2Nzdigifi9Ecm9wYm94L2xhYi9lbWVyZ2VudC1jb2V4aXN0ZW5jZS9kYXRhL3RlbXAvY29tbXVuaXRpZXNfYWJ1bmRhbmNlLmNzdiIsIGNvbF90eXBlcyA9IGNvbHMoKSkKY29tbXVuaXRpZXNfYWJ1bmRhbmNlCmBgYAoKCiMjIE1hdGNoIGNvbW11bml0eSBFU1ZzIGFuZCBpc29sYXRlIFNhbmdlciBzZXF1ZW5jZXMKCmBgYHtyfQojIEl0IG1heSB0YWtlIDEwIG1pbnMKaWYgKHJ1bl9zY3JpcHRzKSBzb3VyY2UoInNjcmlwdC8wMUUtbWF0Y2hfY29tbXVuaXR5X2FidW5kYW5jZS0wMi1tYXRjaF9pc29sYXRlXzE2Uy5SIikKYGBgCgpJVVBBQyBub3RhdGlvbiBmb3IgRE5BIGJhc2UgcGFpcnMKClcsIFMsIE0sIEssIFIsIFkgcmVwcmVzZW50IHR3byBwb3NzaWJpbGl0aWVzIGZvciBvbmUgYmFzZSBwYWlyIEIsIEQsIEgsIFYgcmVwcmVzZW50IHRocmVlIHBvc3NpYmxpdGllcyBOIG1lYW5zIGFueSBudWNsZW90aWRlcyAoYnV0IG5vdCBhIGdhcCkKCkkgdGVzdGVkIGZpdmUgYWxpZ25tZW50IG1ldGhvZHMgaW4gYEJpb3N0cmluZ3M6OnBhaXJ3aXNlQWxpZ25tZW50KClgCgpgYGB7ciBlY2hvID0gVH0Kc2VxdWVuY2VzX2FsaWdubWVudCA8LSByZWFkX2Nzdigifi9Ecm9wYm94L2xhYi9lbWVyZ2VudC1jb2V4aXN0ZW5jZS9kYXRhL3RlbXAvc2VxdWVuY2VzX2FsaWdubWVudC5jc3YiLCBjb2xfdHlwZSA9IGNvbHMoKSkKc2VxdWVuY2VzX2FsaWdubWVudApgYGAKCgojIyBSZWxhdGl2ZSBhYnVuZGFuY2UgZXhwbGFpbmVkIGJ5IHRoZSBzZXF1ZW5jZXMKCmBgYHtyIG1lc3NhZ2UgPSBGLCB3YXJuaW5nID0gRn0KaWYgKHJ1bl9zY3JpcHRzKSBzb3VyY2UoInNjcmlwdC8wMUUtbWF0Y2hfY29tbXVuaXR5X2FidW5kYW5jZS0wMy1tYXRjaGVkX2FidW5kYW5jZS5SIikKYGBgCgpFU1YgYWJ1bmRhbmNlcyBvZiAxMyBjb21tdW5pdGllcyB1c2VkIGluIHRoZSBjb21wZXRpdGlvbiBhc3NheQoKYGBge3IgZmlnLndpZHRoID0gNSwgZmlnLmhlaWdodCA9IDN9CnBsb3RfYWJ1bmRhbmNlIDwtIGZ1bmN0aW9uKGRmLCBsYWJlbF94ID0gIkNvbW11bml0eSIsIGxhYmVsX3kgPSAiUmVsYXRpdmVBYnVuZGFuY2UiLCBmaWxsID0gIkNvbW11bml0eUVTVklEIikgewogICAgZ2dwbG90KGRmKSArCiAgICAgICAgZ2VvbV9iYXIoYWVzX3N0cmluZyh4ID0gbGFiZWxfeCwgeSA9IGxhYmVsX3ksIGZpbGwgPSBmaWxsKSwKICAgICAgICAgICAgICAgICBwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQgPSAiaWRlbnRpdHkiLCBjb2wgPSAxKSArCiAgICAgICAgdGhlbWVfYncoKSArCiAgICAgICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpICsKICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSkKfQpjb21tdW5pdGllc19hYnVuZGFuY2UgPC0gcmVhZF9jc3YoIn4vRHJvcGJveC9sYWIvZW1lcmdlbnQtY29leGlzdGVuY2UvZGF0YS90ZW1wL2NvbW11bml0aWVzX2FidW5kYW5jZS5jc3YiLCBjb2xfdHlwZXMgPSBjb2xzKCkpCmNvbW11bml0aWVzX2FidW5kYW5jZSAlPiUgcGxvdF9hYnVuZGFuY2UobGFiZWxfeCA9ICJDb21tdW5pdHkiLCBsYWJlbF95ID0gIlJlbGF0aXZlQWJ1bmRhbmNlIiwgZmlsbCA9ICJFU1ZGYW1pbHkiKQpgYGAKCkFidW5kYW5jZXMgb2YgRVNWcyBtYXRjaGVkIHRvIFNhbmdlcnMKCmBgYHtyfQppc29sYXRlc19hYnVuZGFuY2UgPC0gcmVhZF9jc3YoIn4vRHJvcGJveC9sYWIvZW1lcmdlbnQtY29leGlzdGVuY2UvZGF0YS90ZW1wL2lzb2xhdGVzX2FidW5kYW5jZS5jc3YiLCBjb2xfdHlwZXMgPSBjb2xzKCkpCmlzb2xhdGVzX1JEUCA8LSByZWFkX2Nzdigifi9Ecm9wYm94L2xhYi9lbWVyZ2VudC1jb2V4aXN0ZW5jZS9kYXRhL3RlbXAvaXNvbGF0ZXNfUkRQLmNzdiIsIGNvbF90eXBlcyA9IGNvbHMoKSkKIyBDb2xvciBzZXRzIGZyb20gU3lsdmllCmNvbG9yX3NldHMgPC0gdGliYmxlKENvbG9yID0gYygieWVsbG93IiwgImRlZXBza3libHVlMyIsICJibHVlIiwgImRhcmtvcmNoaWQyIiwgImZpcmVicmljayIsICJvcmFuZ2UyIiwgImdyZXkiKSwKICBGYW1pbHkgPSBjKCJBZXJvbW9uYWRhY2VhZSIsICJFbnRlcm9iYWN0ZXJpYWNlYWUiLCAiTW9yYXhlbGxhY2VhZSIsICJQc2V1ZG9tb25hZGFjZWFlIiwiQ29tYW1vbmFkYWNlYWUiLCJBbGNhbGlnZW5hY2VhZSIsICJTcGhpbmdvYmFjdGVyaWFjZWFlIikpCgppc29sYXRlc19hYnVuZGFuY2UgJT4lCiAgICBsZWZ0X2pvaW4oaXNvbGF0ZXNfUkRQKSAlPiUKICAgIG11dGF0ZShDb21tdW5pdHkgPSBvcmRlcmVkKENvbW11bml0eSwgIGNvbW11bml0aWVzJENvbW11bml0eSkpICU+JQogICAgcGxvdF9hYnVuZGFuY2UobGFiZWxfeCA9ICJDb21tdW5pdHkiLCBsYWJlbF95ID0gIiBSZWxhdGl2ZUFidW5kYW5jZSIsIGZpbGwgPSAiRmFtaWx5IikgKwogICAgbGFicyh4ID0gIiIsIHkgPSAiUmVsYXRpdmUgYWJ1bmRhbmNlIikgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gc2V0TmFtZXMoY29sb3Jfc2V0cyRDb2xvciwgY29sb3Jfc2V0cyRGYW1pbHkpKSArCiAgICBzY2FsZV94X2Rpc2NyZXRlKGV4cGFuZD1jKDAsMCkpICsKICAgIHNjYWxlX3lfY29udGludW91cyhleHBhbmQ9YygwLDApLCBsaW1pdHMgPSBjKDAsMSksIGJyZWFrcyA9IHNlcSgwLDEsIC4yNSkpIAoKYGBgCgoKIyBTdW1tYXJ5CgojIyBDb21iaW5lIGFuZCBnZW5lcmF0ZSB0aGUgaXNvbGF0ZSBtZXRhZGF0YSBmaWxlCgpgYGB7cn0KaWYgKHJ1bl9zY3JpcHRzKSBzb3VyY2UoInNjcmlwdC8wMS1pc29sYXRlcy0wMS1yZWFkX21hdGNoX2lzb2xhdGUuUiIpCmBgYAoKSXNvbGF0ZXMgaW5mb3JtYXRpb24gaW4gb25lIGRhdGEuZnJhbWUgYGlzb2xhdGVzYC4gNjggaXNvbGF0ZXMKCmBgYHtyfQppc29sYXRlcyA8LSByZWFkX2Nzdigifi9Ecm9wYm94L2xhYi9lbWVyZ2VudC1jb2V4aXN0ZW5jZS9kYXRhL291dHB1dC9pc29sYXRlcy5jc3YiLCBjb2xfdHlwZXMgPSBjb2xzKCkpCmlzb2xhdGVzCmBgYAoKCgoKCiMjIFBsb3QgaXNvbGF0ZXMnIGZ1bmN0aW9ucyAoVGhpZXMgc2VjdGlvbiBpcyBkZXByZWNhdGVkKQoKIyMjIEdyb3d0aCByYXRlcyBvbiB2YXJpZWQgQ1NzCgpgYGB7cn0KaXNvbGF0ZXMgJT4lCiAgICBzZWxlY3QoSUQsIEZlcm1lbnRlciwgc3RhcnRzX3dpdGgoInJfIikpICU+JQogICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKC1GZXJtZW50ZXIsIC1JRCksIG5hbWVzX3ByZWZpeCA9ICJyXyIsIG5hbWVzX3RvID0gIkNTIiwgdmFsdWVzX3RvID0gInIiKSAlPiUKICAgIG11dGF0ZShDUyA9IGZhY3RvcihDUywgYygiZ2x1Y29zZSIsICJhY2V0YXRlIiwgImxhY3RhdGUiLCAic3VjY2luYXRlIiwgImdsdWNvbmF0ZSIsICJrZXRvZ2x1Y29uYXRlIikpKSAlPiUKICAgIGZpbHRlcighaXMubmEoRmVybWVudGVyKSkgJT4lCiAgICBnZ3Bsb3QoKSArCiAgICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IHIsIGZpbGwgPSBGZXJtZW50ZXIpLCBjb2xvciA9IDEsIGJpbndpZHRoID0gMC4wMDUpICsKICAgIGZhY2V0X3dyYXAoLn5DUywgbnJvdyA9IDMsIHNjYWxlcyA9ICJmcmVlX3kiKSArCiAgICB0aGVtZV9jbGFzc2ljKCkKYGBgCgojIyMgT0QgYXQgMTZociBvbiB2YXJpZWQgQ1NzCgpgYGB7cn0KaXNvbGF0ZXMgJT4lCiAgICBzZWxlY3QoSUQsIEZlcm1lbnRlciwgc3RhcnRzX3dpdGgoIk9EIikpICU+JQogICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKC1GZXJtZW50ZXIsIC1JRCksIG5hbWVzX3ByZWZpeCA9ICJPRDYyMF8xNmhfIiwgbmFtZXNfdG8gPSAiQ1MiLCB2YWx1ZXNfdG8gPSAiciIpICU+JQogICAgbXV0YXRlKENTID0gZmFjdG9yKENTLCBjKCJnbHVjb3NlIiwgImFjZXRhdGUiLCAic3VjY2luYXRlIiwgImNpdHJhdGUiLCAiZnJ1Y3Rvc2UiLCAiYXJhYmlub3NlIiwgImdhbGFjdG9zZSIsICJyaWJvc2UiLCAiZ2x5Y2Vyb2wiLCAicHlydXZhdGUiLCAibWFsYXRlIiwgImZ1bWFyYXRlIikpKSAlPiUKICAgIGZpbHRlcighaXMubmEoRmVybWVudGVyKSkgJT4lCiAgICBnZ3Bsb3QoKSArCiAgICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IHIsIGZpbGwgPSBGZXJtZW50ZXIpLCBjb2xvciA9IDEsIGJpbndpZHRoID0gMC4wMSkgKwogICAgZmFjZXRfd3JhcCgufkNTLCBucm93ID0gNCwgc2NhbGVzID0gImZyZWVfeSIpICsKICAgIHRoZW1lX2NsYXNzaWMoKQpgYGAKCgojIyMgQWNpZHMgcHJvZHVjdGlvbgpgYGB7cn0KaXNvbGF0ZXMgJT4lCiAgICBzZWxlY3QoSUQsIEZlcm1lbnRlciwgc3RhcnRzX3dpdGgoIlhfIiksIHN0YXJ0c193aXRoKCJwSCIpKSAlPiUKICAgIHBpdm90X2xvbmdlcihjb2xzID0gYygtRmVybWVudGVyLCAtSUQpLCBuYW1lc19wcmVmaXggPSAiWF8iLCBuYW1lc19zZXAgPSAiXyIsIG5hbWVzX3RvID0gYygiQ1MiLCAiVGltZSIpLCB2YWx1ZXNfdG8gPSAiQW1vdW50IikgJT4lCiAgICBtdXRhdGUoVGltZSA9IHN1YigiaHIiLCAiIiwgVGltZSkpICU+JQogICAgbXV0YXRlKENTID0gZmFjdG9yKENTLCBjKCJwSCIsICJhY2V0YXRlIiwgImxhY3RhdGUiLCAic3VjY2luYXRlIikpKSAlPiUKICAgIGZpbHRlcighaXMubmEoRmVybWVudGVyKSkgJT4lCiAgICBnZ3Bsb3QoKSArCiAgICBnZW9tX2xpbmUoYWVzKHggPSBUaW1lLCB5ID0gQW1vdW50LCBncm91cCA9IElELCBjb2xvciA9IEZlcm1lbnRlcikpICsKICAgIGZhY2V0X3dyYXAoLn5DUywgbnJvdyA9IDIsIHNjYWxlcyA9ICJmcmVlX3kiKSArCiAgICB0aGVtZV9jbGFzc2ljKCkKYGBgCgoKCgoKCgoKCgoKCgoKCgo=